﻿// Stickers
// An example app demonstrating cloud native .NET apps
// Written by daxnet (Sunny Chen)
// MIT License
// 2024

using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json.Nodes;

namespace Stickers.Common.Authorization;

public sealed class KeycloakPermissionService(
    IHttpClientFactory httpClientFactory,
    ILogger<KeycloakPermissionService> logger) : IPermissionService
{
    public async Task<IEnumerable<Claim>?> ReadPermissionClaimsAsync(string bearerToken, string audience,
        string requestUri)
    {
        logger.LogDebug($"Parameters: bearerToken: {bearerToken}, audience: {audience}, requestUri: {requestUri}");
        var result = new List<Claim>();
        using var httpClient = httpClientFactory.CreateClient("JwtTokenClient");
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
        var payload = new Dictionary<string, string>
        {
            { "grant_type", "urn:ietf:params:oauth:grant-type:uma-ticket" },
            { "audience", audience }
        };

        var request = new HttpRequestMessage(HttpMethod.Post, requestUri)
        {
            Content = new FormUrlEncodedContent(payload)
        };

        try
        {
            var response = await httpClient.SendAsync(request);
            logger.LogDebug($"Reading permission claims response status code: {response.StatusCode}");
            response.EnsureSuccessStatusCode();
            var responseJson = await response.Content.ReadAsStringAsync();
            logger.LogDebug($"Reading permission claims response content: {responseJson}");
            var responseJsonObject = JsonNode.Parse(responseJson);
            var authTokenString = responseJsonObject?["access_token"]?.GetValue<string>();
            if (string.IsNullOrEmpty(authTokenString))
                return null;

            var tokenHandler = new JwtSecurityTokenHandler();
            var authToken = tokenHandler.ReadJwtToken(authTokenString);
            var authClaim = authToken.Claims.FirstOrDefault(a => a.Type == "authorization");
            if (authClaim is null)
                return null;

            var authObject = JsonNode.Parse(authClaim.Value);
            if (authObject?["permissions"] is not JsonArray permissionsArray)
                return null;

            foreach (var permissionObj in permissionsArray)
            {
                var accessibleResource = permissionObj?["rsname"]?.GetValue<string>();
                if (string.IsNullOrEmpty(accessibleResource))
                    continue;
                var allowedScopes = new List<string?>();
                var scopesObj = permissionObj?["scopes"];
                if (scopesObj is JsonArray scopesArray)
                {
                    allowedScopes.AddRange(scopesArray.Select(s => s?.GetValue<string>())
                        .Where(val => !string.IsNullOrEmpty(val)));
                }

                result.Add(new Claim($"res:{accessibleResource}",
                    string.Join(",", allowedScopes)));
            }

            return result;
        }
        catch
        {
            return null;
        }
    }
}